import { Tools } from './../../../tools/tools';
import { DataRPermits } from './../../../../models/dataRPermits';
import { PermitItem } from 'src/app/models/permitItem';
import {
  AfterViewInit,
  ChangeDetectorRef,
  Component,
  ComponentFactoryResolver,
  ComponentRef,
  ElementRef,
  OnDestroy,
  OnInit,
  Type,
  ViewChild,
  ViewContainerRef
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ZeprideTemplateRenderService } from 'src/app/common/zepride-template/zepride-template.service';
import { ExceptionHandler } from 'src/app/handler/exception.handler';
import {
  Condition,
  DataRegister,
  FlowNode,
  NodePath,
  NodePathCondition,
  Page,
  PageTmpItem,
  TmpComItem,
  Workflow,
  WorkflowOrder,
  WorkflowOrderNodesItem
} from 'src/app/models';
import { FlowNodeService, TemplateService, PageService, DataRegisterService, WorkflowService } from 'src/app/services';
import { OneColumnComponent } from '../../page-layouts/one-column/one-column.component';
import { PageLayoutCommonComponent } from '../../page-layouts/page-layout-common.component';
import { SudokuComponent } from '../../page-layouts/sudoku/sudoku.component';
import { Algorithm, Graph } from '@antv/g6';
import { WorkflowPageService } from '../workflow-page.service';
import { IEdge, INode } from '@antv/g6/lib/interface/item';
import { getDataModel } from '../../page-render-tool';
import { EngineService } from 'src/app/common/engines/services/engine.service';
import { NzMessageService } from 'ng-zorro-antd';
import { compareDataField } from 'src/app/common/tools/condition-tool';
import { EventManagerService } from 'src/app/services/event-manager.service';
import { Subscription } from 'rxjs';
import { TemplateLayoutComponent } from 'src/app/common/zepride-template/template-layout/template-layout.component';
import { ConditionService } from 'src/app/common/services';
import * as Enumerable from 'linq';
import { AuthorityService } from 'src/app/common/authority/authority.service';
import { UserService } from 'src/app/services/user.service';
import { DataChangeLogService } from 'src/app/services/data-change-log.service';
import Node from '@antv/g6/lib/item/node';

@Component({
  selector: 'zepride-flownode-page',
  templateUrl: './flownode-page.component.html',
  styleUrls: ['./flownode-page.component.less']
})
export class FlownodePageComponent implements OnInit, AfterViewInit, OnDestroy {

  flowNodeId = '';
  flowNode: FlowNode;
  workflow: Workflow;
  @ViewChild('containerRef', { read: ViewContainerRef }) containerRef: ViewContainerRef;
  layoutComponentRef: ComponentRef<PageLayoutCommonComponent>;
  pageId = '';
  page: Page;
  /**
   * 模板数据模型
   */
  get dataModel() {
    return this.engineService.currentDataModel;
  }

  get mapDataModel() {
    return this.engineService.mapDataModel;
  }

  dataRegisterId = Tools.GetGuid();
  saving = false;
  dataRegister: DataRegister;
  drUserIds: string[] = [];
  drPermits: DataRPermits[] = [];
  dataRegisterVersion: number;

  auditBtnDisable = false;
  showAuditBtn = false;
  hasAudit = false;
  auditOption: { submitHandle: () => void; };
  conditionChange$: Subscription;
  fileMakerMakeEventId = '';
  fileMakeOption: {
    showFilePerview?: boolean;
    fileInfo?: {
      fileName: string;
      url: string;
    }
  } = {};
  hideBtnPanel = false;
  pageBtnShowOrHideSub: Subscription;
  auditBtnClickSub: Subscription;
  auditStartedSubscription: Subscription;
  auditCurrentItemChangedSub: Subscription;
  onSaveDataSub: Subscription;

  involvedPersonChangeSubscription: Subscription;
  /**
   * 节点是否是只读节点
   */
  isReadOnlyNode = false;
  /**
   * 节点是否可以开启
   */
  canNodeBeOpened = true;
  /**
   * 节点是否可以关闭
   */
  canNodeBeClosed = true;
  /**
   * 创建子流程的消息订阅
   */
  createChildFlow$: Subscription;
  /**
   * 删除子流程的消息订阅
   */
  deleteChildFlow$: Subscription;

  childDataRegister: DataRegister;

  mode: 'edit' | 'view' = 'edit';

  isLastNode = false;
  isFinished = false;
  isSuperEdit = false;
  isSuperEditMode = false;
  /**
   * 节点不具备权限
   */
  isUnAuthorized = true;
  renderComplete$: Subscription;
  rendering = false;
  renderProgress: {
    [key: string]: boolean;
  };
  @ViewChild('frameEle') set frameEle(ref: ElementRef<HTMLFrameElement>) {
    this.frameEleRef = ref;
  }
  frameEleRef: ElementRef<HTMLFrameElement>;
  @ViewChild('cAreaPage') set cAreaPage(ref: ElementRef<HTMLDivElement>) {
    this.cAreaPageRef = ref;
  }
  cAreaPageRef: ElementRef<HTMLDivElement>;
  constructor(
    private activatedRoute: ActivatedRoute,
    private flowNodeService: FlowNodeService,
    private tplService: TemplateService,
    private tplRenderService: ZeprideTemplateRenderService,
    private pageService: PageService,
    private changeRef: ChangeDetectorRef,
    private componentFactoryResolver: ComponentFactoryResolver,
    private workflowPageService: WorkflowPageService,
    private workflowService: WorkflowService,
    private dataRegisterService: DataRegisterService,
    private engineService: EngineService,
    private message: NzMessageService,
    private router: Router,
    private conditionService: ConditionService,
    private eventSvr: EventManagerService,
    private authorityService: AuthorityService,
    private userService: UserService,
    private dataChangeLogSvr: DataChangeLogService
  ) { }

  ngOnInit() {
    const mode: any = this.activatedRoute.snapshot.queryParamMap.get('mode');
    this.mode = mode ? mode : 'edit';
    this.activatedRoute.paramMap.subscribe(async paramMap => {
      try {
        this.showAuditBtn = false;
        this.hideBtnPanel = false;
        this.isSuperEditMode = false;
        this.rendering = true;
        this.engineService.currentDataModel = {} as any;
        this.exitFileMake();
        const workflowId = this.activatedRoute.parent.snapshot.paramMap.get('workflowId');
        await this.initWorkflowById(workflowId);
        this.flowNodeId = paramMap.get('flowNodeId');
        const dataRegisterId = this.activatedRoute.snapshot.queryParamMap.get('dataRegisterId');
        this.clearLayout();
        const flowNode = await this.getFlowNodeById(this.flowNodeId);
        this.flowNode = flowNode;
        this.workflowPageService.currentNodeId = this.flowNodeId;
        let extra: any;
        const data = await this.initDataRegister(dataRegisterId);
        this.getProjectAuthorities(data);
        if (data && data.id) {
          this.workflowService.setDataRegisterId(data.id);
          if (data.data) {
            extra = data.data.fields;
            this.dataRegisterVersion = data.version;
            if (data.data.flowNodesItems) {
              extra.curNodeNames = data.data.flowNodesItems.filter(x => x.status == 'process').map(x => x.nodeName).join(',');
              this.workflowPageService.notifyFlowNodeUpdate(data.data.flowNodesItems.find(x => x.flowNodeId == this.flowNodeId), flowNode);
            }
          }
          if (this.workflow) {
            this.workflow.workflowNodeItems = this.workflowService.updateNodeByDataRegister(this.dataRegister, this.workflow);
          }
          this.workflowPageService.notifyWorkflowUpdated(data);
        } else {
          this.workflowPageService.notifyWorkflowUpdated({ id: this.dataRegisterId });
        }
        this.canNodeBeOpened = this.checkCanNodeBeOpened(flowNode);
        this.isReadOnlyNode = this.checkIsReadOnlyNode(this.dataRegister, flowNode);

        if (!flowNode.pages || flowNode.pages.length == 0) {
          this.generateDataModel([], extra);
          this.rendering = false;
          this.message.error('当前节点未配置页面！');
        } else {
          const pageId = flowNode.pages[0].id;
          const page = await this.getPageById(pageId);
          this.page = page;
          this.pageService.currentPage = page;
          if (extra) {
            extra.dataChangeLog = await this.dataChangeLogSvr.getMergedDataChangeLog(dataRegisterId);
          }
          this.generateDataModel(this.page.pageTmpItems, extra);
          this.layoutComponentRef.instance.page = page;
          this.setConditions();
          this.generateProgressData(page);
        }
        this.engineService.clear();
        this.isLastNode = this.checkIsLastNode(this.flowNodeId);
        this.isFinished = this.checkIsFinished();
      } catch (error) {
        ExceptionHandler.current.handleError(error);
        this.rendering = false;
      }
    });
    this.auditStartEventHandle();
    const auditFinishEventId = this.eventSvr.registEvent('audit.finish', () => {
      this.eventSvr.removeEvent(auditFinishEventId);
      this.showAuditBtn = false;
      this.changeRef.detectChanges();
    });
    this.fileMakerMakeEventHandle();
    this.involvedPersonChangeEventHandle();
    this.pageBtnShowOrHideSub = this.pageService.pageBtnShowOrHide$.subscribe(isHide => {
      this.hideBtnPanel = isHide;
    });
    this.auditBtnClickSub = this.pageService.auditBtnClick$.subscribe(() => {
      this.subAudit();
    });
    this.onSaveDataSub = this.workflowPageService.onSaveData$.subscribe(() => {
      const workflowOrder = this.generateWorkflowOrder();
      return this.saveData(workflowOrder);
    });
    this.isSuperEdit = this.authorityService.isSuperEdit;
  }

  private auditStartEventHandle() {
    this.auditStartedSubscription = this.workflowPageService.auditInit.subscribe(() => {
      this.showAuditBtn = true;
      this.changeRef.detectChanges();
    });
    this.auditCurrentItemChangedSub = this.workflowPageService.auditCurrentItemChanged$.subscribe(auditItem => {
      if (auditItem.permissionDenied) {
        // 表示当前无审核此步骤的权限
        this.auditBtnDisable = true;
      } else {
        this.auditBtnDisable = false;
      }
    });
  }

  private fileMakerMakeEventHandle() {
    this.fileMakerMakeEventId = this.eventSvr.registEvent('fileMaker.make', (data) => {
      this.fileMakeOption.showFilePerview = true;
      this.fileMakeOption.fileInfo = data;
      setTimeout(async () => {
        const url = data.fileUrl;
        let fileType = 'word';
        let fcode = 4;
        if (data.fileName.indexOf('doc') > -1) {
          fileType = 'word';
        } else if (data.fileName.indexOf('xls') > -1) {
          fileType = 'excel';
        } else if (data.fileName.indexOf('pdf') > -1) {
          fcode = 3;
        }
        const r: any = await this.pageService.makeFile(this.dataModel, url, fcode, fileType);
        this.fileMakeOption.fileInfo.url = r.data;
        if (this.frameEleRef && this.frameEleRef.nativeElement) {
          this.frameEleRef.nativeElement.src = `https://view.officeapps.live.com/op/view.aspx?src=${r.data}`;
        }
      }, 1000);
    });
  }

  private involvedPersonChangeEventHandle() {
    this.involvedPersonChangeSubscription = this.workflowService.involvedPersonChange$.subscribe((data: { permitList: PermitItem[]; userId: string; }[]) => {
      this.drUserIds = data.map(x => x.userId);
      this.drUserIds = Enumerable.from(this.drUserIds).distinct(x => x).toArray();
      data.forEach(item => {
        if (Array.isArray(item.permitList)) {
          item.permitList.forEach((pitem: any) => {
            const datarPermit: DataRPermits = {
              id: Tools.GetGuid(),
              refObjectId: pitem.refObjectId,
              refObjectType: pitem.refObjectType,
              authObjectId: pitem.authObjectId,
              authObjectType: pitem.authObjectType,
              dataRegisterId: '',
              orgId: '',
              permitId: pitem.permitId,
              permitName: pitem.permitName,
              accountId: pitem.accountId
            };
            this.drPermits.push(datarPermit);
          });
        }
      });
      this.drPermits = Enumerable.from(this.drPermits).distinct(x => x.accountId + x.permitId + x.refObjectId).toArray();
    });
  }

  exitFileMake() {
    this.fileMakeOption.showFilePerview = false;
    this.eventSvr.callEvents('fileMaker.make.cancel', {});
  }

  ngAfterViewInit() {
    this.initLayoutContainer();
    this.onComponentRenderComplete();
  }
  /**
   * 根据布局类型，初始化页面容器
   */
  initLayoutContainer() {
    const ref = this.renderLayoutContainer();
    this.initLayoutContainerEvent(ref);
    this.changeRef.detectChanges();
  }

  async initWorkflowById(workflowId: string) {
    if (workflowId) {
      this.workflowPageService.workflowId = workflowId;
      if (this.workflowService.currentWorkflow && this.workflowService.currentWorkflow.id === workflowId) {
        this.workflow = this.workflowService.currentWorkflow;
      } else {
        const workflow = await this.workflowService.getWorkflowById(workflowId);
        this.workflow = workflow;
        this.workflowService.currentWorkflow = workflow;
      }
      this.sortWorkflowNodes();
    }
  }

  clearLayout() {
    if (this.layoutComponentRef) {
      this.initLayoutContainer();
    }
  }
  /**
   * 获取已存在的dataRegister的数据
   * @param dataRegisterId 父dataRegisterId
   */
  async initDataRegister(dataRegisterId: string) {
    const childDataRegisterId = this.workflowPageService.currentChildDataRegisterId;
    let parentDataRegister: DataRegister;
    let childDataRegister: DataRegister;
    if (childDataRegisterId) {
      childDataRegister = await this.dataRegisterService.getDataRegister(childDataRegisterId);
    }
    if (dataRegisterId) {
      const data = await this.dataRegisterService.getDataRegister(dataRegisterId);
      let currentDataRegisterId = dataRegisterId;
      if (data && data.parentDataRegisterId) {
        parentDataRegister = await this.dataRegisterService.getDataRegister(data.parentDataRegisterId);
        currentDataRegisterId = data.parentDataRegisterId;
        childDataRegister = data;
      } else {
        parentDataRegister = data;
      }
      this.workflowPageService.currentDataRegisterId = currentDataRegisterId;
    }
    if (parentDataRegister && parentDataRegister.id) {
      let data = parentDataRegister.data;
      if (childDataRegister && childDataRegister.id) {
        data = this.mergeDataRegisterData(parentDataRegister, childDataRegister);
        // data = { ...data, ...childDataRegister.data };
      }
      this.dataRegister = { ...parentDataRegister, data };
      this.dataRegisterId = dataRegisterId;
    } else if (!dataRegisterId && !childDataRegisterId) {
      await this.initEmptyDataRegister();
    }
    return this.dataRegister;
  }
  /**
   * 是否是子流程节点
   * @params flowNodeId 节点id
   */
  isChildFlowNode(flowNodeId: string): boolean {
    const node = this.workflow.workflowNodeItems.find(item => item.flowNodeId === flowNodeId);
    return !!node.flowNode.childrenWorkflowId;
  }
  /**
   * 合并子流程数据
   */
  mergeDataRegisterData(parentDataregister: DataRegister, childDataRegister: DataRegister) {
    const parentData = parentDataregister.data;
    const childData = childDataRegister.data;
    // const parentItems = parentData.flowNodesItems;
    // const childItems = childData.flowNodesItems;
    // const flowNodesItems = [];
    // for (const item of parentItems) {
    //   const isChildFlowNode = this.isChildFlowNode(item.flowNodeId);
    //   if (isChildFlowNode) {
    //     const flowNodeItem = childItems.find(childItem => childItem.flowNodeId === item.flowNodeId);
    //     if (flowNodeItem) {
    //       flowNodesItems.push(flowNodeItem);
    //     }
    //   } else {
    //     flowNodesItems.push(item);
    //   }
    // }
    const fields = { ...parentData.fields, ...childData.fields };
    return { ...parentData, ...childData, fields };
  }
  /**
   * 获取项目权限
   */
  getProjectAuthorities(dataRegister: DataRegister) {
    if (!dataRegister) {
      return;
    }
    const authorities = dataRegister.datarPermits;
    if (authorities && authorities.length) {
      const account = this.userService.account;
      const accountId = account ? account.id : '';
      const auths = authorities.filter(auth => auth.accountId === accountId);
      this.authorityService.projectAuthorities = auths;
    }
  }
  /**
   * 当前节点不存在dataRegister时，初始化一个
   */
  async initEmptyDataRegister(parentRegisterId?: string) {
    const workflowOrder = this.generateWorkflowOrder();
    const index = workflowOrder.flowNodesItems.findIndex(item => item.flowNodeId === this.flowNodeId);
    if (index > -1) {
      workflowOrder.flowNodesItems[index].status = 'process';
    }
    const dataRegister: DataRegister = {
      id: this.dataRegisterId,
      objectId: this.workflow.id,
      objectType: 'workflow',
      dataType: 'workflowOrder',
      data: workflowOrder
    };
    if (this.drUserIds && this.drUserIds.length) {
      dataRegister.userIds = this.drUserIds;
    }
    if (parentRegisterId) {
      dataRegister.parentDataRegisterId = parentRegisterId;
    }
    // const data = await this.dataRegisterService.createDataRegister(dataRegister, this.drPermits);
    this.dataRegister = dataRegister;
    this.dataRegisterId = dataRegister.id;
    this.workflowPageService.currentDataRegisterId = this.dataRegister.id;
  }

  getTemplates() {
    return this.tplService.getTemplates();
  }

  getFlowNodeById(flowNodeId: string) {
    return this.flowNodeService.getFlowNodeById(flowNodeId);
  }

  getPageById(pageId: string) {
    return this.pageService.getPageById(pageId);
  }
  /**
   * 动态渲染模板
   * @param templateId 模板的id
   * @param container 容器
   * @param config 模板配置
   */
  renderTemplate(templateId: string, container: HTMLDivElement, config?: any) {
    const tempIndex = this.page.pageTmpItems.findIndex(temp => temp.templateId === templateId);
    if (tempIndex === -1) {
      return;
    }
    const templateData = this.page.pageTmpItems[tempIndex];
    let tplInvalid = false;
    if (templateData.template.condition) {
      const isValid = this.conditionService.checkCondition(this.dataModel, templateData.template.condition);
      if (!isValid) {
        tplInvalid = true;
        container.style.display = 'none';
      }
    }
    container.dataset.templateId = templateId;
    const componentRef = this.tplRenderService.renderTemplateToDom(container);
    templateData.template.tmpComItems.sort((prev, next) => {
      const prevIndex = prev.index ? prev.index : 0;
      const nextIndex = next.index ? next.index : 0;
      return prevIndex - nextIndex;
    });
    this.setComponentDisabled(templateData.template.tmpComItems);
    const initData: PageTmpItem = {
      templateId,
      ...templateData,
      dataModel: this.dataModel,
      tplInvalid,
      mode: this.mode
    };
    componentRef.instance.pageTmpItem = initData;
    componentRef.instance.config = config;
    container.dataset.tplname = templateData.template.name;
    this.onTemplateConditionEvent(componentRef, container);
    return componentRef;
  }
  /**
   * 数据变化时，判断模板应该显示还是隐藏
   */
  onTemplateConditionEvent(componentRef: ComponentRef<TemplateLayoutComponent>, tplContainer: HTMLDivElement) {
    this.conditionChange$ = componentRef.instance.conditionChangeEvent.subscribe((condition: Condition) => {
      if (condition) {
        if (condition.result) {
          tplContainer.style.display = 'block';
          componentRef.instance.componentRefs.forEach(com => {
            com.instance.componentData.tplInvalid = false;
          });
        } else {
          tplContainer.style.display = 'none';
          componentRef.instance.componentRefs.forEach(com => {
            com.instance.componentData.tplInvalid = true;
          });
        }
      }
    });
  }
  /**
   * 汇总当前模板的条件
   */
  setConditions() {
    if (Array.isArray(this.page.pageTmpItems) && this.page.pageTmpItems.length) {
      const items = [];
      this.page.pageTmpItems.forEach(tpl => {
        if (tpl.template.condition && tpl.template.condition.sentence) {
          items.push(tpl.template.condition);
        }
        if (tpl.template.tmpComItems) {
          for (const tmpComItem of tpl.template.tmpComItems) {
            if (tmpComItem.condition) {
              items.push(tmpComItem.condition);
            }
          }
        }
      });
      this.conditionService.conditions = items;
    }
  }
  /**
   * 获取布局类型
   * @param layoutName 布局类型
   */
  getLayoutComponent(layoutName: string) {
    let result: Type<any>;
    switch (layoutName) {
      case 'sudoku':
        result = SudokuComponent;
        break;
      case 'oneColumn':
        result = OneColumnComponent;
        break;
    }
    return result;
  }
  /**
   * 初始化页面容器
   */
  renderLayoutContainer(): ComponentRef<PageLayoutCommonComponent> {
    const component = this.getLayoutComponent('oneColumn');
    if (!component) {
      return;
    }
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
    const viewContainerRef = this.containerRef;
    viewContainerRef.clear();
    const componentRef = viewContainerRef.createComponent(componentFactory);
    this.layoutComponentRef = componentRef;
    return componentRef;
  }

  initLayoutContainerEvent(componentRef: ComponentRef<PageLayoutCommonComponent>) {
    componentRef.instance.renderTemplate = this.renderTemplate.bind(this);
  }
  /**
   * 生成页面的数据模型
   * @param template 模板
   */
  generateDataModel(pageTmpItems: PageTmpItem[], extra?: any) {
    if (Array.isArray(pageTmpItems) && pageTmpItems.length) {
      pageTmpItems.forEach(tpl => {
        const data = getDataModel(tpl);
        this.engineService.currentDataModel = {
          ...this.dataModel,
          ...data,
          ...extra
        };
      });
    } else {
      this.engineService.currentDataModel = {
        ...this.dataModel,
        ...extra
      };
    }
  }
  /**
   * 获取上一步节点的id
   */
  getPrevNodeId() {
    const nodeId = this.flowNodeId;
    const items: { flowNodeId: string; status: 'finish' | 'process' }[] = this.dataRegister.data.flowNodesItems;
    const paths = this.workflow.paths;
    const nodeInPaths = paths.filter(path => path.endNodeId === nodeId);
    if (Array.isArray(nodeInPaths) && nodeInPaths.length) {
      const prevNodeIds = nodeInPaths.map(path => path.headNodeId);
      const prevNodeItem = items.find(item => item.status === 'finish' && prevNodeIds.indexOf(item.flowNodeId) > -1);
      if (prevNodeItem) {
        return prevNodeItem.flowNodeId;
      }
    }
    return '';
  }
  /**
   * 构建当前节点状态数据
   * @param isNext 是否是 下一步
   * @param validPath 满足条件的节点连线
   */
  generateNodeItems(isNext?: boolean, validPaths?: NodePath[]) {
    let nodeItems: WorkflowOrderNodesItem[] = [];
    if (this.dataRegister && this.dataRegister.data.flowNodesItems) {
      nodeItems = this.dataRegister.data.flowNodesItems;
      const index = nodeItems.findIndex(item => item.flowNodeId === this.flowNodeId);
      if (index > -1) {
        nodeItems[index].status = 'process';
      }
    } else {
      const nodeInfos = this.workflow.workflowNodeItems.map(item => {
        return {
          flowNodeId: item.flowNodeId,
          nodeName: item.flowNode.name
        };
      });
      nodeItems = nodeInfos;
    }
    if ((isNext && validPaths && validPaths.length) || this.isLastNode) {
      nodeItems.forEach(item => {
        if (item.flowNodeId === this.flowNodeId) {
          item.status = this.canNodeBeClosed ? 'finish' : 'unclosed';
          if (item.status == 'finish') {
            item.updateTime = Date.now();
            const loginUser = this.workflowService.loginUser;
            item.executor = {
              userId: loginUser.id,
              name: loginUser.name
            };
          }
        }
        else if (validPaths && validPaths.findIndex(path => path.endNodeId === item.flowNodeId) > -1) {
          item.status = item.status === 'unclosed' ? 'unclosed' : 'process';
        }
      });
    }
    nodeItems.forEach(item => {
      const target = this.workflow.workflowNodeItems.find(nodeItem => item.flowNodeId === nodeItem.flowNodeId);
      if (target) {
        item.disabled = target.disabled;
      }
    });
    return nodeItems;
  }
  /**
   * 构建流程表单数据
   * @param isNext 是否是 下一步
   * @param validPath 满足条件的节点之间的连线
   */
  generateWorkflowOrder(isNext?: boolean, validPaths?: NodePath[]) {
    const flowNodesItems = this.generateNodeItems(isNext, validPaths);
    const workflowOrder: WorkflowOrder = {
      workflowId: this.workflow.id,
      fields: { ...this.dataModel },
      flowNodesItems
    };
    return workflowOrder;
  }
  /**
   * 上一步
   */
  async prev() {
    try {
      const valid = await this.validateComponents();
      if (!valid) {
        return;
      }
      const workflowOrder = this.generateWorkflowOrder();
      const data = await this.save(workflowOrder);
      this.workflowPageService.notifyWorkflowUpdated(data);
      const prevNodeId = this.getPrevNodeId();
      if (prevNodeId && data) {
        this.navToNodeById(prevNodeId);
      } else {
        throw new Error('未找到满足条件的上一个节点');
      }
    } catch (error) {
      ExceptionHandler.current.handleError(error);
    }
  }
  /**
   * 下一步
   */
  async next() {
    try {
      if (!this.canNodeBeOpened) {
        throw new Error(`节点不满足开启条件：${this.flowNode.openCondition.sentence}`);
      }
      const valid = await this.validateComponents();
      if (!valid) {
        return;
      }
      const fields = this.dataModel;
      const paths = this.getPathsByDirection(fields, 'out');
      const { validPaths } = paths;
      const disabledResults = this.getNodesWithStatus(paths);
      this.canNodeBeClosed = this.checkCanNodeBeClosed(this.flowNode);
      let workflowOrder = this.generateWorkflowOrder(true, validPaths);
      workflowOrder = await this.closeUnClosedNodes(workflowOrder);
      const data = await this.save(workflowOrder);
      this.workflowPageService.notifyWorkflowUpdated(data);
      this.workflowPageService.notifyNodeValidChanged(disabledResults);
      if (this.workflow.refPageId) {
        this.navToRefPage(this.workflow.refPageId);
      } else {
        if (this.isLastNode) {
          location.reload();
        } else {
          if (validPaths && validPaths.length && data) {
            const nextNodeId = validPaths[0].endNodeId;
            this.navToNodeById(nextNodeId);
          } else {
            throw new Error('未找到满足条件的下个节点');
          }
        }
      }
    } catch (error) {
      ExceptionHandler.current.handleError(error);
    }
  }
  /**
   * 关闭满足条件的未关闭的节点
   */
  async closeUnClosedNodes(workflowOrder: WorkflowOrder) {
    const items = workflowOrder.flowNodesItems;
    for (const item of items) {
      if (item.status === 'unclosed') {
        const flowNode = await this.getFlowNodeById(item.flowNodeId);
        const canBeClosed = this.checkCanNodeBeClosed(flowNode);
        item.status = canBeClosed ? 'finish' : 'unclosed';
      }
    }
    return workflowOrder;
  }
  /**
   * 判断节点是否可用
   * @param paths 路径
   */
  getNodesWithStatus(paths: { invalidPaths: NodePath[]; validPaths: NodePath[]; }): {
    id: string;
    name: string;
    disabled: boolean;
  }[] {
    const graph = this.workflowPageService.pageGraph;
    const { breadthFirstSearch } = Algorithm;
    const result = {};
    const { validPaths } = paths;
    const { invalidPaths } = paths;
    // 判断之后节点是否可用，以可用(false)为优先级最高
    validPaths.forEach(path => {
      const startNodeId = path.endNodeId;
      breadthFirstSearch(graph, startNodeId, {
        enter: ({ current, previous }) => {
          const nodeId = current.getID();
          result[nodeId] = false;
        }
      });
    });
    invalidPaths.forEach(path => {
      const startNodeId = path.endNodeId;
      breadthFirstSearch(graph, startNodeId, {
        enter: ({ current, previous }) => {
          const nodeId = current.getID();
          const has = Object.prototype.hasOwnProperty.call(result, nodeId);
          if (!has) {
            result[nodeId] = true;
          }
        }
      });
    });
    const nodes = this.workflow.workflowNodeItems;
    nodes.forEach(node => {
      const nodeId = node.flowNodeId;
      const has = Object.prototype.hasOwnProperty.call(result, nodeId);
      if (has && !node.disabled) {
        node.disabled = result[nodeId];
      }
    });
    return nodes.map(node => {
      return { id: node.flowNodeId, name: node.flowNode.name, disabled: node.disabled };
    });
  }

  /**
   * 提交审核按钮
   *
   */
  async subAudit() {
    try {
      const valid = await this.validateComponents();
      if (!valid) {
        return;
      }
      this.workflowPageService.auditSubmit.next();
      await this.saveDraft();
    } catch (error) {
      ExceptionHandler.current.handleError(error);
    }
  }

  /**
   * 保存
   */
  save(workflowOrder: WorkflowOrder) {
    return this.saveData(workflowOrder);
  }

  /**
   * 保存草稿
   */
  saveDraft() {
    const workflowOrder = this.generateWorkflowOrder();
    const index = workflowOrder.flowNodesItems.findIndex(item => item.flowNodeId === this.flowNodeId);
    if (index > -1) {
      workflowOrder.flowNodesItems[index].status = 'process';
    }
    return this.saveData(workflowOrder);
  }
  /**
   * 保存数据（dataRegister）
   * @param workflowOrder 流程表单数据
   */
  async saveData(workflowOrder: WorkflowOrder) {
    try {
      this.saving = true;
      let dataRegisterId = '';
      let parentDataRegisterId = '';
      if (this.flowNode.childrenWorkflowId && this.workflowPageService.currentChildDataRegisterId) {
        dataRegisterId = this.workflowPageService.currentChildDataRegisterId;
        parentDataRegisterId = this.dataRegisterId;
      } else {
        dataRegisterId = this.dataRegisterId;
      }
      const dataRegister: DataRegister = {
        id: dataRegisterId,
        objectId: this.workflow.id,
        objectType: 'workflow',
        dataType: 'workflowOrder',
        data: workflowOrder,
        version: this.dataRegisterVersion
      };
      if (this.isLastNode) {
        dataRegister.status = 'complete';
      }
      if (parentDataRegisterId) {
        dataRegister.parentDataRegisterId = parentDataRegisterId;
      }
      if (this.drUserIds && this.drUserIds.length) {
        dataRegister.userIds = this.drUserIds;
      }

      const data: DataRegister = await this.dataRegisterService.createDataRegister(dataRegister, this.drPermits);
      this.dataRegister = data;
      this.message.success('保存成功');
      this.isSuperEditMode = false;
      return data;
    } catch (error) {
      // ExceptionHandler.current.handleError(error);
      throw error;
    } finally {
      this.saving = false;
    }
  }

  /**
   * 获取满足条件和不满足条件的路径
   * @param fields 表单字段
   * @param direction 路径方向
   */
  getPathsByDirection(fields: any, direction: 'in' | 'out'): { validPaths: NodePath[]; invalidPaths: NodePath[]; } {
    const currentNode = this.getCurrentGraphNode();
    const outPaths = this.getNodePaths(currentNode, direction);
    const validPaths: NodePath[] = [];
    const invalidPaths: NodePath[] = [];
    if (Array.isArray(outPaths) && outPaths.length) {
      for (const path of outPaths) {
        const isValid = this.validateNodeCondition(fields, path.condition);
        if (isValid) {
          validPaths.push(path);
        } else {
          invalidPaths.push(path);
        }
      }
    }
    return { validPaths, invalidPaths };
  }
  /**
   * 导航到目标节点
   * @param nodeId 目标节点id
   */
  navToNodeById(nodeId: string) {
    this.workflowPageService.currentNodeId = nodeId;
    this.router.navigate([`home/page/workflow-page/${this.workflow.id}/${nodeId}`], {
      queryParams: { dataRegisterId: this.dataRegisterId }
    });
  }
  /**
   * 获取当前的节点
   */
  getCurrentGraphNode() {
    const graph = this.workflowPageService.pageGraph;
    if (!graph) { return; }
    const node: INode = graph.findById(this.flowNodeId) as INode;
    return node;
  }
  /**
   * 获取当前节点的连线
   */
  getNodePaths(node: INode, direction: 'in' | 'out'): NodePath[] {
    let edges: IEdge[] = [];
    if (direction === 'in') {
      edges = node.getInEdges();
    } else if (direction === 'out') {
      edges = node.getOutEdges();
    }
    let result: NodePath[] = [];
    if (Array.isArray(edges) && edges.length) {
      const paths = this.workflow.paths;
      result = paths.filter(path => {
        const index = edges.findIndex(edge => edge.getSource().getID() === path.headNodeId && edge.getTarget().getID() === path.endNodeId);
        return index > -1;
      });
    }
    return result;
  }
  /**
   * 校验数据是否满足指定的条件
   * @param dataRegister 数据
   * @param condition 条件
   */
  validateNodeCondition(dataRegister: DataRegister, condition: NodePathCondition) {
    if (!condition) {
      return true;
    }
    return compareDataField(dataRegister, condition);
  }
  /**
   * 校验当前页面的组件
   */
  async validateComponents() {
    const validateParams = {
      objectId: this.workflow.id,
      dataType: 'workflowOrder',
      objectType: 'workflow' as any
    };
    const result = await this.engineService.validateComponents(validateParams);
    if (result && result.length) {
      const msg = result.map(x => x.errorMsg).filter(x => x).join(',');
      this.message.error(`验证失败${msg ? '【' + msg + '】' : ''}，请检查表单！`);
      return false;
    } else {
      return true;
    }
  }

  destoryData() {
    this.engineService.clear();
    this.authorityService.projectAuthorities = [];
    this.dataRegister = null;
    this.workflowPageService.currentChildDataRegisterId = '';
    this.workflowPageService.currentDataRegisterId = '';
    this.workflowService.currentWorkflow = null;
    this.workflowPageService.notifyWorkflowUpdated(null);
    this.workflowPageService.notifyFlowNodeUpdate(null, null);
  }

  fullScreen() {
    const isFullscreen = document.fullscreenElement;
    const element = this.cAreaPageRef;
    if (element && element.nativeElement) {
      if (isFullscreen) {
        document.exitFullscreen();
      } else {
        element.nativeElement.requestFullscreen();
      }
    }
  }

  downloadFile() {
    if (this.fileMakeOption.fileInfo.url) {
      window.open(this.fileMakeOption.fileInfo.url);
    }
  }
  /**
   * 判断节点是否只读
   * @param dataRegister 数据
   * @param flowNode 待判断的节点
   */
  checkIsReadOnlyNode(dataRegister: DataRegister, flowNode: FlowNode) {
    if (!dataRegister || !dataRegister.data || !dataRegister.data.flowNodesItems) {
      return false;
    } else {
      const targetNode: {
        flowNodeId: string;
        nodeName: string;
        status: string;
      } = dataRegister.data.flowNodesItems.find((node: { flowNodeId: string; }) => node.flowNodeId === flowNode.id);
      if (targetNode && targetNode.status === 'finish') {
        return true;
      } else {
        return false;
      }
    }
  }
  /**
   * 设置组件是否是只读模式
   * @param tmpComItems 组件数组
   */
  setComponentDisabled(tmpComItems: TmpComItem[]) {
    if (!this.isReadOnlyNode && this.canNodeBeOpened) {
      return;
    }
    if (tmpComItems && tmpComItems.length) {
      tmpComItems.forEach(item => {
        item.component.disabled = true;
      });
    }
  }
  /**
   * 节点是否满足开启条件
   */
  checkCanNodeBeOpened(flowNode: FlowNode): boolean {
    const openCondition: Condition = flowNode.openCondition;
    if (openCondition) {
      return this.conditionService.checkCondition(this.dataModel, openCondition);
    } else {
      return true;
    }
  }
  /**
   * 节点是否满足关闭条件
   */
  checkCanNodeBeClosed(flowNode: FlowNode): boolean {
    const closeCondition: Condition = flowNode.closeCondition;
    if (closeCondition) {
      return this.conditionService.checkCondition(this.dataModel, closeCondition);
    } else {
      return true;
    }
  }

  checkIsLastNode(flowNodeId: string) {
    const graph = this.workflowPageService.pageGraph;
    if (!graph) {
      return false;
    }
    const graphNode = graph.findById(flowNodeId) as Node;
    if (!graphNode || graphNode.destroyed) {
      return false;
    }
    const outeEdges = graphNode.getOutEdges();
    if (!outeEdges || !outeEdges.length) {
      return true;
    } else {
      return false;
    }
  }

  checkIsFinished() {
    const dataRegisterFinished = this.dataRegister && this.dataRegister.status === 'complete';
    if (!dataRegisterFinished) {
      const flownodeId = this.flowNodeId;
      if (this.dataRegister && this.dataRegister.data && this.dataRegister.data.flowNodesItems) {
        const node = this.dataRegister.data.flowNodesItems.find(item => item.flowNodeId === flownodeId);
        return node.status === 'finish';
      } else {
        return false;
      }
    } else {
      return true;
    }
  }

  /**
   * 获取第一个节点
   * @param workflow 流程
   */
  getFirstNode(workflow: Workflow) {
    const edges = workflow.paths;
    const nodes = workflow.workflowNodeItems;
    const targetIds = edges.map(edge => edge.endNodeId);
    const firstNode = nodes.find(node => targetIds.indexOf(node.flowNodeId) === -1);
    return firstNode;
  }

  /**
   * 按广度对节点排序
   * @param graph 图表对象
   */
  sortNodesByBreadth(graph: Graph): Promise<INode[]> {
    if (!graph) {
      return;
    }
    const firstNode = this.getFirstNode(this.workflow);
    const { breadthFirstSearch } = Algorithm;
    const nodesLength = graph.getNodes().length;
    const result: INode[] = [];
    return new Promise((resolve) => {
      breadthFirstSearch(graph, firstNode.flowNodeId, {
        enter: (params) => {
          const current = params.current;
          result.push(current);
          if (result.length === nodesLength) {
            resolve(result);
          }
        }
      });
    });
  }

  sortWorkflowNodes() {
    this.workflow.workflowNodeItems.sort((prevItem, nextItem) => {
      const prevIndex = prevItem.flowNode.extends.index;
      const nextIndex = nextItem.flowNode.extends.index;
      return prevIndex - nextIndex;
    });
  }

  navToRefPage(pageId: string) {
    if (pageId) {
      this.router.navigate(['home/page/normal-page', this.workflow.refPageId]);
    }
  }
  /**
   * 具备超级编辑权限的用户，发出修改当前数据的通知
   */
  superAuthEdit() {
    if (this.isSuperEdit) {
      this.isSuperEditMode = true;
      this.engineService.notifySuperEdit();
    }
  }
  /**
   * 组件渲染进度数据构建
   * @param page 当前页面
   */
  generateProgressData(page: Page) {
    const data = {};
    if (page && page.pageTmpItems && page.pageTmpItems.length) {
      page.pageTmpItems.forEach(pageTmpItem => {
        const tmpItems = pageTmpItem.template.tmpComItems;
        if (tmpItems && tmpItems.length) {
          tmpItems.forEach(tmpItem => {
            data[tmpItem.id] = false;
          });
        }
      });
    }
    this.renderProgress = data;
    const keys = Object.keys(data);
    if (!keys || !keys.length) {
      this.rendering = false;
    }
  }
  /**
   * 监听组件渲染完成的通知
   */
  onComponentRenderComplete() {
    this.renderComplete$ = this.engineService.renderComplete$.subscribe(componentRef => {
      const data = componentRef.instance.componentData;
      const tmpComItemId = data.tmpComItemId;
      this.renderProgress[tmpComItemId] = true;
      this.rendering = !this.getIsRenderComplete();
    });
  }
  /**
   * 判断当前节点是否渲染完成
   */
  getIsRenderComplete(): boolean {
    const values = Object.values(this.renderProgress);
    if (Array.isArray(values) && values.length) {
      return values.every(value => value);
    } else {
      return true;
    }
  }

  ngOnDestroy() {
    this.destoryData();
    this.eventSvr.removeEvent(this.fileMakerMakeEventId);
    if (this.involvedPersonChangeSubscription) {
      this.involvedPersonChangeSubscription.unsubscribe();
    }
    if (this.conditionChange$) {
      this.conditionChange$.unsubscribe();
    }
    if (this.auditStartedSubscription) {
      this.auditStartedSubscription.unsubscribe();
    }
    if (this.pageBtnShowOrHideSub) {
      this.pageBtnShowOrHideSub.unsubscribe();
    }
    if (this.auditBtnClickSub) {
      this.auditBtnClickSub.unsubscribe();
    }
    if (this.onSaveDataSub) {
      this.onSaveDataSub.unsubscribe();
    }
    if (this.auditCurrentItemChangedSub) {
      this.auditCurrentItemChangedSub.unsubscribe();
    }
    if (this.renderComplete$) {
      this.renderComplete$.unsubscribe();
    }
  }
}
